﻿using Blm.Utils.Extensions;
using Blm.VisionCloud.FrameWork.Cached;
using Language.Resource;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using VisionCloud.Api.Auth;
using VisionCloud.Core.Model.Sys;
using VisionCloud.Domain.Common;
using VisionCloud.Domain.DtoModel.Sys;
using VisionCloud.Service.Sys;
using VisionCloud.Utility;
using VisionCloud.Utility.Extensions;
using VisionCloud.Utility.Security;

namespace VisionCloud.Api.Controllers
{
    public class AuthBaseControlller : ApiBaseController
    {
        /// <summary>
        /// 用户服务
        /// </summary>
        public UserService UserService { get; set; }

        /// <summary>
        /// 缓存
        /// </summary>
        public ICached Cached { get; set; }

        public ILogger<AuthBaseControlller> logger { get; set; }

        /// <summary>
        /// 多语言资源
        /// </summary>
        public IStringLocalizer<ControllerResource> localizer { get; set; }

        /// <summary>
        /// 生成Jwt Token
        /// </summary>
        /// <returns></returns>
        protected async Task<string> GetToken(SysUserDto modelDto, string jsCode = null)
        {
            var userCode = modelDto.Email ?? modelDto.PhoneNumber;
            modelDto.Code = userCode;
            var pwd = modelDto.Password;
            try
            {
                RSACryptoService rsa = new RSACryptoService(RSACryptoService.DefaultPrivateKey, RSACryptoService.DefaultPublicKey);
                modelDto.Code = rsa.Decrypt(modelDto.Code);
                modelDto.Password = rsa.Decrypt(pwd);
                pwd = modelDto.Password;
            }
            catch (Exception)
            {
                throw new OperationException(localizer["YB0024"].Value);
            }

            if (string.IsNullOrEmpty(userCode))
            {
                throw new OperationException(localizer["YB0025"].Value);
            }

            if (string.IsNullOrEmpty(pwd))
            {
                throw new OperationException(localizer["YB0026"].Value);
            }
            UserService.CheckLockUser(modelDto, false, true);

            string clientType = modelDto.TokenType.ToString();
            string logId = $"{clientType}:{modelDto.Code}:{HttpContext.GetClientUserIp()}";
            var user = await UserService.FindUser(modelDto);
            var cacheExp = modelDto.TokenType == TokenTypeEnum.Applet ? TimeSpan.FromDays(30) : TimeSpan.FromDays(1);

            if (user != null)
            {
                user.TokenType = modelDto.TokenType;
                Cached.Set(logId, user, cacheExp, true);
                //RedisHelper.Instance.SetValue(logId, user.ToJsonStr(), TimeSpan.FromDays(1));
            }

            if (user == null)
            {
                //await LogService.Login(modelDto, null);
                throw new OperationException(localizer["YB0027"].Value);
            }

            if (user.IsLock || user.Status == SysUser.EnumUserStatus.禁用)
            {
                throw new OperationException(localizer["YB0028"].Value);
            }

            string tokenStr = await CreateJwtToken(user, logId, modelDto.TokenType, jsCode);

            logger.LogInformation(string.Format(localizer["YB0033"].Value, modelDto.Code.SetSensitive()));

            string resultStr = $"Bearer {tokenStr}";

            if (modelDto.TokenType != TokenTypeEnum.Applet) this.SetCookiet(CookiesHelper.CookiesName, tokenStr);

            return !user.IsPasswordNeedToChange ? resultStr : string.Format(localizer["YB0029"].Value, resultStr);
        }

        /// <summary>
        /// 生成Jwt Token,密码信息为密文
        /// </summary>
        /// <param name="modelDto"></param>
        /// <param name="dogNum"></param>
        /// <param name="isSurfaceLogin"></param>
        /// <returns></returns>
        /// <exception cref="OperationException"></exception>
        protected async Task<string> GetTokenWithSurface(SysUserDto modelDto, string dogNum = null, bool isSurfaceLogin = false)
        {
            var userCode = modelDto.Email ?? modelDto.PhoneNumber;
            modelDto.Code = userCode;

            string logId = $"surface:{userCode}:{HttpContext.GetClientUserIp()}";
            SysUserDto user;
            if (string.IsNullOrEmpty(dogNum))
                user = await UserService.FindUser2(modelDto);
            else
                user = await UserService.FindUser(modelDto, dogNum);

            if (user == null)
            {
                throw new OperationException(localizer["YB0027"]);
            }

            Cached.Set(logId, user, TimeSpan.FromDays(1), true);

            if (user.IsLock)
            {
                throw new OperationException(localizer["YB0028"]);
            }
            if (!isSurfaceLogin && user.Status == SysUser.EnumUserStatus.禁用)
            {
                throw new OperationException(localizer["YB0028"].Value);
            }
            var tokenStr = await CreateJwtToken(user, logId, modelDto.TokenType);

            this.SetCookiet(CookiesHelper.CookiesName, tokenStr);

            logger.LogInformation(string.Format(localizer["YB0033"].Value, user.AccountNumber.SetSensitive()));

            return tokenStr;
        }

        /// <summary>
        /// 创建JwtToken
        /// </summary>
        /// <param name="user"></param>
        /// <param name="logId"></param>
        /// <param name="tokenType"></param>
        /// <param name="jsCode"></param>
        /// <returns></returns>
        protected async ValueTask<string> CreateJwtToken(SysUserDto user, string logId, TokenTypeEnum tokenType, string jsCode = null)
        {
            var claims = new[]
            {
                // 注意：拓展参数考虑新旧版本兼容
                new Claim("YunUserGuid", user.YunUserGuid.ToString()),
                new Claim("Name", user.Name),
                new Claim("Code", user.Name),
                new Claim("Role", user.RoleList.IsEmpty() ? string.Empty : string.Join(",",user.RoleList)),
                new Claim("LogId",logId),
                new Claim("Department", user.Department.IsEmpty() ? string.Empty : string.Join(",",user.Department)),
                new Claim("CompanyGuid", user.SysOrgIds.IsEmpty()? string.Empty : string.Join(",",user.SysOrgIds)),
                new Claim("OrgId", user.SysOrgIds.IsEmpty()? string.Empty : string.Join(",",user.SysOrgIds)),
                new Claim("OrgCode", user.SysOrgCodes.IsEmpty() ? string.Empty : string.Join(",",user.SysOrgCodes)),
                new Claim("RoleCode", user.RoleCodeList.IsEmpty() ? string.Empty : string.Join(",",user.RoleCodeList)),
                new Claim("RoleNameList", user.RoleNameList.IsEmpty() ? string.Empty : string.Join(",",user.RoleNameList)),
                new Claim("UserType", user.UserType?.NullToString()),
                new Claim("TokenId", Guid.NewGuid().ToString()),
                new Claim("Company", !string.IsNullOrEmpty(user.Company) ? user.Company : string.Empty),
            };
            if (tokenType == TokenTypeEnum.Applet && !string.IsNullOrEmpty(jsCode))
            {
                var openid = await WeChartApiHelper.GetWxOpenId(jsCode);
                claims = claims.Append(new Claim("WxOpenId", openid)).ToArray();
                var cacheKey = $"Applet:{user.YunUserGuid}:OpenId";
                Cached.Set(cacheKey, openid, TimeSpan.FromDays(30), true);
            }
            var tokenExp = tokenType == TokenTypeEnum.Applet ? DateTime.Now.AddDays(30) : AuthCore.GetExpires();
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthCore.SecurityKey));
            var token = new JwtSecurityToken(
                //颁发者
                issuer: AuthCore.Domain,
                //接收者
                audience: Guid.NewGuid().ToString(), //todo 这边可以优化
                expires: tokenExp, //过期时间
                signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256), //签名证书
                claims: claims//自定义参数
            );
            var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
            return tokenStr;
        }

        /// <summary>
        /// 获取游客用户的Token
        /// </summary>
        /// <exception cref="OperationException"></exception>
        protected async Task<string> GetTokenForVistor(TokenTypeEnum tokenType, string jsCode = null)
        {
            var vistor = await UserService.SysUserRepository.FirstOrDefaultAsync(x => !x.IsDeleted && x.UserType == SysUser.EnumUserType.游客账号);
            if (vistor == null)
            {
                throw new OperationException("暂不支持游客用户");
            }
            var openId = string.Empty;
            if (tokenType == TokenTypeEnum.Applet && !string.IsNullOrEmpty(jsCode))
            {
                openId = await WeChartApiHelper.GetWxOpenId(jsCode);
            }
            var logId = string.IsNullOrEmpty(openId) ? $"{tokenType}:{vistor.PhoneNumber ?? vistor.Email}:{HttpContext.GetClientUserIp()}" : $"{tokenType}:{vistor.PhoneNumber ?? vistor.Email}:{openId}";

            var dto = vistor.CopyTo<SysUserDto>();
            dto.RoleNameList = vistor.RoleNameList.ToObject<List<string>>() ?? new List<string>();
            dto.RoleList = new List<Guid>();
            dto.RoleCodeList = new List<string>();
            dto.SysOrgIds = (new string[] { vistor.SysOrgId }).ToList();
            dto.SysOrgCodes = (new string[] { vistor.CompanyNo }).ToList();
            Cached.Set(logId, dto, TimeSpan.FromDays(1), true);

            var claims = new[]
            {
                    // 注意：拓展参数考虑新旧版本兼容
                    new Claim("YunUserGuid", vistor.YunUserGuid.ToString()),
                    new Claim("Name", vistor.Name),
                    new Claim("Code", vistor.Name),
                    new Claim("Role", string.Join(",",vistor.RoleNameList)),
                    new Claim("LogId",logId),
                    new Claim("Department",string.Join(",",vistor.Department)),
                    new Claim("CompanyGuid",string.Join(",",vistor.SysOrgId)),
                    new Claim("OrgId",string.Join(",",vistor.SysOrgId)),
                    new Claim("OrgCode",string.Join(",",vistor.CompanyNo)),
                    new Claim("RoleCode",string.Join(",",vistor.RoleNameList)),
                    new Claim("RoleNameList",string.Join(",",dto.RoleNameList)),
                    new Claim("UserType", vistor.UserType.ToString()),
                    new Claim("TokenId",Guid.NewGuid().ToString()),
                    new Claim("Company",!string.IsNullOrEmpty(vistor.Company) ? vistor.Company : string.Empty),
                    new Claim("WxOpenId",openId),
            };

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthCore.SecurityKey));
            var token = new JwtSecurityToken(
                //颁发者
                issuer: AuthCore.Domain,
                //接收者
                audience: Guid.NewGuid().ToString(), //todo 这边可以优化
                expires: AuthCore.GetExpires(), //过期时间
                signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256), //签名证书
                claims: claims//自定义参数
            );
            var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);

            string resultStr = $"Bearer {tokenStr}";
            //this.SetCookiet(CookiesHelper.CookiesName, tokenStr);

            return resultStr;
        }
    }
}